<?php
namespace qhweb\phpsm\ecc\sm3;

/**
 * SM3 摘要算法基类 GeneralDigest
 *
 * 提供消息填充、块处理、长度编码等通用功能，供 SM3 等哈希实现继承。
 * 采用大端字节序，内部维护 64 字节（512 位）消息块缓冲区与已处理字节计数。
 *
 * @package     qhweb\phpsm\ecc\sm3
 * @author      qhweb
 * @version     1.0.0
 */
class GeneralDigest 
{
    /**
     * 消息块字节长度（单位：字节）
     * @var int
     */
    private const  BYTE_LENGTH = 64;
    /**
     * 输入缓冲区（未处理消息字节）
     * @var int[]
     */
    private $xBuf=array();
    /**
     * 缓冲区中已填充的字节数（下一个写入位置）
     * @var int
     */
    private $xBufOff;
    /**
     * 已处理的消息总字节数
     * @var int
     */
    private $byteCount;

    /**
     * 从另一个 GeneralDigest 实例复制内部状态
     *
     * @param  GeneralDigest $t 源摘要对象
     * @return void
     */
    public function setGeneralDigest($t)
    {
        $this->arraycopy($t->xBuf, 0, $this->xBuf, 0, sizeof($t->xBuf));

        $this->xBufOff = $t->xBufOff;
        $this->byteCount = $t->byteCount;
    }
    /**
     * 构造方法
     *
     * 初始化缓冲区与计数。注意：此处仅初始化了前 4 个字节为 0，
     * 其余位置将依赖后续写入或重置逻辑进行初始化。
     */
    public function __construct( )
    {
        $this->xBuf[0]=0; 
        $this->xBuf[1]=0;
        $this->xBuf[2]=0; 
        $this->xBuf[3]=0;
    }

    /**
     * 输入一个字节
     *
     * @param  int $input 输入字节（0–255）
     * @return void
     */
    public function Update($input)
    {
        $this->xBuf[$this->xBufOff++] = $input;

        if ($this->xBufOff == sizeof($this->xBuf))
        {
            $this->ProcessWord($this->xBuf, 0);
            $this->xBufOff = 0;
        }

       $this->byteCount++;
    }
    /**
     * 输入字节数组的指定区间
     *
     * @param  array $input  字节数组（索引从 0 开始）
     * @param  int   $inOff  起始偏移（包含）
     * @param  int   $length 输入长度（字节）
     * @return void
     */
    public function BlockUpdate($input, $inOff, $length): void
    {
        // 1) 先填满当前未满的缓冲区
        while (($this->xBufOff != 0) && ($length > 0))
        {
            $this->Update($input[$inOff]);
            $inOff++;
            $length--;
        }

        // 2) 处理整块数据
        while ($length > sizeof($this->xBuf))
        {
            $this->ProcessWord($input, $inOff);
            $inOff += sizeof($this->xBuf);
            $length -= sizeof($this->xBuf);
           $this->byteCount += sizeof($this->xBuf);
        }

        // 3) 处理剩余不足一块的字节
        while ($length > 0)
        {
            $this->Update($input[$inOff]);
            $inOff++;
            $length--;
        }
    }
    /**
     * 完成哈希计算前的收尾处理
     *
     * 1) 附加比特 '1'（0x80）
     * 2) 填充 0 直至长度 ≡ 56 (mod 64) 字节
     * 3) 附加 64 位大端消息长度（比特数）
     * 4) 处理最终块
     *
     * @return void
     */
    public function Finish(): void
    {
        // 计算消息总比特长度（左移 3 位等价于乘以 8）
        $bitLength = $this->LeftRotateLong($this->byteCount , 3);
        // 附加 0x80（二进制 10000000）
        $this->Update(128);
        // 填充 0，直到缓冲区刚好剩 8 字节（用于长度）
        while ($this->xBufOff != 0) $this->Update(0);
        $this->ProcessLength($bitLength);
        $this->ProcessBlock();
    }

    /**
     * 重置内部状态，便于复用对象进行新的哈希计算
     *
     * @return void
     */
    public function Reset(): void
    {
        $this->byteCount = 0;
        $this->xBufOff = 0;
        $this->xBuf[0]=0; 
        $this->xBuf[1]=0;
        $this->xBuf[2]=0; $this->xBuf[3]=0;
    }
    /**
     * 获取基础块长度（字节）
     *
     * @return int 块长度（固定为 64）
     */
    public function GetByteLength(): int
    {
        return $this::BYTE_LENGTH;
    }

     /**
     * 将指定长度的源数组内容复制到目标数组
     *
     * 注：为兼容未显式声明的辅助方法，此处保留原签名。
     * 建议实现为：
     *   protected function arraycopy(array $src, int $srcPos, array &$dest, int $destPos, int $length): void
     *
     * @param  array $src     源数组
     * @param  int   $srcPos  源起始位置
     * @param  array &$dest   目标数组（引用）
     * @param  int   $destPos 目标起始位置
     * @param  int   $length  复制长度
     * @return void
     */
    private function arraycopy($src, $srcPos, &$dest, $destPos, $length): void
    {
        // TODO: 建议显式实现数组复制逻辑，避免依赖未声明方法
        for ($i = 0; $i < $length; $i++) {
            $dest[$destPos + $i] = $src[$srcPos + $i];
        }
    }

    /**
     * 处理一个 32 位字（大端）
     *
     * 注：由子类按具体哈希算法实现（如 SM3 的扩展、压缩函数等）。
     *
     * @param  array $word  输入字（长度通常为 4 字节）
     * @param  int   $inOff 字在输入中的偏移
     * @return void
     */
    protected function ProcessWord($word, $inOff): void
    {
        // TODO: 由子类实现
    }

    /**
     * 处理消息长度（以比特为单位，大端 64 位）
     * @param  int $bitLength 消息总比特长度
     * @return void
     */
    protected function ProcessLength($bitLength): void
    {
        // TODO: 由子类实现
    }

    /**
     * 处理当前块（通常为 64 字节）
     * @return void
     */
    protected function ProcessBlock(): void
    {
        // TODO: 由子类实现
    }

    /**
     * 32 位循环左移
     *
     * @param int $lValue 待旋转值
     * @param int $lBits  左移位数（0–31）
     * @return int 旋转结果
     */
    public function LeftRotateLong($lValue, $lBits): int
	{
        $lBits = $lBits % 32;
        $lValue=$lValue<< $lBits;
        $lValue=$lValue & 0xffffffff;
        if($lValue>=0x80000000){
            $lValue=$lValue-(0xffffffff+1);
        }
        return  $lValue;
	}
}